/*
 * @(#)AnaFunction.java
 *
 * Revision:
 * Author                                         Date           
 * --------------------------------------------   ------------   
 * Ana Emilia Victor Barbosa Coutinho             25/06/2012    
 */
package br.edu.ufcg.splab.techniques.reduction.functions.similarity;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import br.edu.ufcg.splab.core.InterfaceEdge;
import br.edu.ufcg.splab.generator.TestCase;



public class SimilarityFunction implements DistanceFunction {

	/**
	 * Retrieve the similarity (number of repeated elements) in two specified paths.
	 * 
	 * @param path1 first test case.
	 * @param path2 second test case.
	 * @return similarity's value between the two paths. 
	 */
	public double getSimilarity(TestCase path1, TestCase path2)
	{
		if(path1.equals(path2)){
			return 0.00;
		}
		return calculateSimilarity(path1, path2);
	}
	
	/**
     * Provide a string representation of the similarity function to be written.
     * @return The string representation of the similarity function.
     */
	public String getFunctionName(){
		return "SF";
	}

	/*
	 * similarity(i,j) = (npi(i,j) + |sia(i,j)|) / ((|i| + |j| + |sda(i)| + |sda(j)|)/2)
	 * 
	 *  sda the set of distinct actions in an test case;
	 *  sia the set of identical actions between two test cases, i.e., the intercession 
	 *        between sda(i) and sda(j);
	 *  npi the number of identical actions pairs between the two test cases.
	 *  
	 *  For example, i = <d, e, f, g, e, c>  and j = <d, e, f, g, e, f, g>, then:
	 *        
	 *        |i| = 6 and |j| = 7
	 *        |sda(i)| = |{d, e, f, g, c}| = 5
	 *        |sda(j)| = |{d, e, f, g}| = 4
	 *        |sia(i,j)| = |{d, e, f, g}| = 4;
	 *        npi(i,j) = 5
	 *        
	 *        
	 *       | sia | # actions | Identical actions pairs  |
	 *       |     |  i  |  j  | (minimun between i and j)|
	 *       ----------------------------------------------
	 *       |  d  |  1  |  1  |            1             |
	 *       |  e  |  2  |  2  |            2             |
	 *       |  f  |  1  |  2  |            1             |
	 *       |  g  |  1  |  2  |         +  1             |
	 *       ---------------------------------------------
	 *       |        npi(i,j) |            5             |
	 *       
	 *   similarity(i,j) = (5 + 4) / ((6 + 7 + 5 + 4)/2) = 9/11 = 0.818           
	 */
	private double calculateSimilarity(TestCase path1, TestCase path2){
		int sizePath1 = path1.getTestCase().size();
		int sizePath2 = path2.getTestCase().size();
		
		Map<String, Integer> mapTC1 = new HashMap<String, Integer>();
		for (InterfaceEdge edge:path1.getTestCase()) {		
			if(mapTC1.containsKey(edge.getLabel())){
				int cont = mapTC1.get(edge.getLabel());
				mapTC1.put(edge.getLabel(), cont + 1);
			}else{
				mapTC1.put(edge.getLabel(), 1);
			}
		}

		Map<String, Integer> mapTC2 = new HashMap<String, Integer>();
		for (InterfaceEdge edge:path2.getTestCase()) {
			if(mapTC2.containsKey(edge.getLabel())){
				int cont = mapTC2.get(edge.getLabel());
				mapTC2.put(edge.getLabel(), cont + 1);
			}else{
				mapTC2.put(edge.getLabel(), 1);
			}			
		}		
		
		int pairs_identical = 0;
		int set_identical = 0;
		
		Set<Entry<String, Integer>> set = mapTC1.entrySet();
		for(Entry<String, Integer> entr : set){
			if(mapTC2.containsKey(entr.getKey())){
				set_identical = set_identical + 1;
				int v = mapTC2.get(entr.getKey());
				if(v > entr.getValue()){
					pairs_identical = pairs_identical + entr.getValue();
				}else{
					pairs_identical = pairs_identical + v;
				}
			}
		}
		
		double avgTotal = (((sizePath1 + sizePath2)/2.00)  + ((mapTC1.size() + mapTC2.size())/2.00))/2.00;
		double similarity = ((pairs_identical + set_identical)/2.00)/avgTotal;
		
		return similarity;
	}
			
}
